home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
dev
/
e
/
amigae21b.lha
/
Amiga_E_v2.1b
/
Docs
/
Tutorial.doc
< prev
next >
Wrap
Text File
|
1992-09-02
|
14KB
|
358 lines
+-----------------------------------------------+
| |
| Amiga E v2.1b |
| Compiler for The E Language |
| By Wouter van Oortmerssen |
| |
| Tutorial |
| |
+-----------------------------------------------+
+-----------------------------------------------+
| LANGUAGE TUTORIAL |
+-----------------------------------------------+
This chapter will take you on a step-by-step guided tour to learn
some E. As soon as you've covered the basics in this section, you may
enhance your knowledge of E using Reference.doc and the examples on
disk. It's best to try the examples with the compiler as you go along.
It is assumed that the reader possesses some kind of knowledge of any
higher programming language (like C/Pascal) to be able to omit the more
obvious explanations. If you feel that you need more explanation about
some feature, see reference.doc.
make sure you have installed Amiga E in your system as described
in 'Compiler.doc' and compiled 'Helloworld.e' as a first test.
Ok, we'll start off easy. Doesn't it strike you as odd that there's
no such command as CLS in the shell? yes? then we'll write our own:
PROC main()
WriteF('\c',12)
ENDPROC
save it as 'cls.e' and compile it with 'ec cls'. then see the result.
nothing shocking so far, but it gets us to two important points.
first of all, you'll notice that even the smallest programs have to
be put in a function, with the name "main". For now it may look
superfluous, but when you start writing bigger programs, you'll notice
why this is. Secondly, we learn something new about WriteF(), and
what the "F" stands for. In compiling HelloWorld.e, you saw that
WriteF() just prints strings to the console. Here we see that such
a string may contain formatting codes introduced by a "\" and a
character that says what type of formatting will take place, like
"c" is for characters, and "d" for decimals. Any arguments to such
a code are simply added as arguments to WriteF(). Here we print
a character with ascii-code 12, which wipes the screen.
And now for something completely different.
Every programmer who starts to learn a new language on the amiga wants
to open his own window. Ok, so lets do that in E.
First we use the E system function OpenW() to open a window:
w:=OpenW(20,11,400,100,$200,$F,'My first window in E!',NIL,1,NIL)
The first few arguments are, of course, the coordinates. The
string must be the title (you can check the exact parameters in
reference.doc chapter 9D). Those two hexadecimal values are flags:
the values for those are defined in the module file 'intuition/intuition'.
There are two ways to use flags like that, by using the module:
MODULE 'intuition/intuition'
idcmpflags:=IDCMP_CLOSEWINDOW
or the lazy way, by just writing down the values:
idcmpflags:=$200
These methods are exact equivalents, and it's up to you to choose which
one you use. Of course, an empty window is boring, so we put a line in it:
Line(20,20,50,50,2)
Line() knows from the last call to OpenW() where to draw it.
We specified $200 (=IDCMP_CLOSEWINDOW) while opening our
window, because we want the user to press the closegadget when he's
ready watching our window. We accomplish this waiting by:
WaitIMessage(w)
This function waits for exactly one message to arrive at our window's
port. And because we told intuition we only want to know about the
close-gadget, this must be IDCMP_CLOSEWINDOW.
Now we can close our window:
CloseW(w)
Putting all this together for our first working window-program:
/* Opening a window in E */
DEF w
PROC main()
IF w:=OpenW(20,11,400,100,$200,$F,'My first window in E!',NIL,1,NIL)
Line(20,20,50,50,2)
WaitIMessage(w)
CloseW(w)
ENDIF
ENDPROC
Save this as 'mywindow.e' and compile it with 'ec mywindow'
You'll notice that there's a small addition to our program: the
IF statement. We do this to check if our window could be opened.
You must _always_ check if you're allocating resources like screens,
windows, libraries and memory! If the assignment in the IF-statement
looks strange to you, remember that OpenW() will return NIL (=0) if
it fails, and then w:=OpenW() will also evaluate to 0, which is FALSE.
Note that if you want to display text in your window, you cannot use
WriteF(), because WriteF() operates on consoles (or files). however,
there's a "F" function that works on rastports (an underlying structure
for windows and screens and the like): TextF().
For example, add after the Line() function:
TextF(20,70,'Well well, i'm writing to my window')
You can even use the Colour() function to change the front- and
background colours of the text.
Now we'll get a bit more complex: we want to have gadgets in our
window to perform actions. Anyone who has used gadgets in other
languages knows that this means filling in tons of structures.
Not so in E: We use the function Gadget() to create gadgets for us.
This function needs buffers to store those structures, so we compute
the required buffersize in a constant:
CONST BUFSIZE=GADGETSIZE*3
where "3" is of course the number of gadgets we wish to create.
"GADGETSIZE" is a system constant that tells us how many bytes
we need to store one gadget.
Then we create the space as a global array:
DEF buf[BUFSIZE]:ARRAY, next
we need the variable next as a sort of dummy with the Gadget() function.
now we can start creating our gadgets:
next:=Gadget(buf,NIL,1,0,20,20,100,'Gadget 1')
next:=Gadget(next,buf,2,0,20,35,100,'Gadget 2')
next:=Gadget(next,buf,3,0,20,50,100,'Gadget 3')
and so our complete program will look like:
/* Opening a window with gadgets in E */
MODULE 'intuition/intuition'
CONST BUFSIZE=GADGETSIZE*3, IFLAGS=IDCMP_CLOSEWINDOW+IDCMP_GADGETUP
DEF buf[BUFSIZE]:ARRAY,next,w,gad:PTR TO gadget
PROC main()
next:=Gadget(buf,NIL,1,0,20,20,100,'Gadget 1')
next:=Gadget(next,buf,2,0,20,35,100,'Gadget 2')
next:=Gadget(next,buf,3,0,20,50,100,'Gadget 3')
IF w:=OpenW(20,11,300,100,IFLAGS,$F,'My first window in E!',NIL,1,buf)
WHILE WaitIMessage(w)<>IDCMP_CLOSEWINDOW
gad:=MsgIaddr()
TextF(20,80,'You pressed gadget #\d',gad.userdata)
ENDWHILE
CloseW(w)
ENDIF
ENDPROC
As you will notice, we now go the straight way of using our intuition
constants by including the module. We define the constant IFLAGS for
all the flags we need: this is now one more, as we also want to hear
about the user pressing gadgets. The OpenW() call has also changed a
little, as the last argument for gadgets, which was NIL in our previous
example, is now filled. Because we now have to respond to two types
of events, our WaitIMessage() construction has changed: the function
returns the class of the event which we didn't use before. Now we'll
only do the WHILE loop for pressed gadgets. We can get a pointer to
the object that caused the event (in our case always a gadget), and
we can look in the .userdata field of the gadget to see which one
was pressed. The value that we find here is exactly the same as what
we wrote as third argument to Gadget(), thus 1,2 or 3. then we
write that value to our window with the TextF() function.
Note that to be able to read fields of a structure relative to a
pointer like "gad", we need to tell the compiler what kind of object
it will be pointing to. We do this with the declaration:
DEF gad:PTR TO gadget
"gad" will still be a variable like the ones we declare like "DEF gad",
but the compiler will now enable us to use it as an object just like
one we create ourselves.
Of course there are lots of other ways to process events. one way is
to make a loop, and have each item receive one message:
class:=WaitIMessage(w)
and then use a "SELECT class" statement to check out all the
different types of classes.
Now we have a pretty good picture of how to write Intuition front-ends
to our programs. Another way of creating even more professional
interfaces that works remarkably well with E is using the 2.0
gadtools library. Using immediate lists and typed lists, you can
create all sorts of gadgets (not just boolean gadgets) with little
more machinery than we used before.
See GadToolsDemo.e for how to create interfaces like that.
To give just a tiny example of how we can write a program that uses
those lists in a powerful fashion we'll write a program that puts up
a requester with a message from the command-line arguments, and returns
0 or 5 (WARN) to dos, according to the gadget selected:
PROC main() RETURN EasyRequestArgs(0,[20,0,0,arg,'ok|cancel'],0,NIL)*5
Yes, indeed, that's the whole program. Note that it only runs on 2.0
or higher. save as 'select.e', and compile 'ec select'. now run:
1> select Select "ok" if you wish to perform this action
This would present the requester and return a value suitable for
tests in scripts ("IF WARN ...").
The compactness of this example lies in the fact that normally functions
like EasyRequestArgs get pointers to structures as arguments,
which have to be built up bit by bit and then given as argument to the
function (see the C-example for this function in the RKRM's to see
what I mean). In E there's the list-feature which is unique for
procedural programming languages which enables you to build complex
data structures 'on-the-fly' without separately defining them and
making identifiers for them.
In this case we have created a structure of type "easystruct" (which is
an object defined in 'intuition/intuition.m'), that is the second argument
to the function, like:
[20,0,0,arg,'ok|cancel']:easystruct
The :<type> specifies the type of the data structure; default is :LONG,
and as all fields of easystruct are again of type LONG we left the ":"
spec. out. Always note that the value of such expressions is a pointer
to the data structure in memory.
As a second example we'll use taglists: we'll open a screen in 2.0
fashion with OpenScreenTaglist():
s:=OpenScreenTagList(0,[SA_DEPTH,8,SA_DISPLAYID,$8020,0])
As you see, we need not build up a taglist structure separately,
nor need we link any varargs hacks.
Next we'll be watching how to use library calls other than those
built in (like EasyRequestArgs() we just saw from intuition):
We'll be using the "asl.library" to pop up a filerequester
(again, this is 2.0 and up only). This example is a short version
of 'AslDemo.e', and is also featured in reference.doc in the
modules chapter. Here we'll explain how it works.
MODULE 'Asl', 'libraries/Asl'
PROC main()
DEF req:PTR TO filerequestr
IF aslbase:=OpenLibrary('asl.library',37)
IF req:=AllocFileRequest()
IF RequestFile(req) THEN WriteF('File: "\s" in "\s"\n',req.file,req.dir)
FreeFileRequest(req)
ENDIF
CloseLibrary(aslbase)
ENDIF
ENDPROC
Until recently, we only used modules to load definitions of constants
and objects; now we'll use modules to define new function calls.
as you see from the module statement, the modules that define
library calls are in the root of emodules: while all other definitions
are in the specific directories (read all about that in reference.doc).
Such a module provides us with a variable for the base address of
the library: usually this is <libname>+"base". in this example,
we have to fill the variable aslbase with a correct value before we
can use its functions, and at the end we have to close it again.
As you can see, the actual usage of the library is very simple.
To hear about the result, we need to check our requester structure
that we got returned from AllocFileRequest() for the members
req.file and req.dir . RequestFile() returns TRUE or FALSE depending
on whether a file was selected in the first place.
There are lots of sources in the examples/ dir that show how to
interface with other libraries in a similar fashion, for example
with the great "ReqTools.library".
In our next example we'll be developing a small utility from
'sources/utilities' step by step: DirQuick.e.
It is a nice and short no-nonsense three column directory-lister.
MODULE 'dos/dos'
PROC main()
DEF info:fileinfoblock,lock,ok,c=0
IF lock:=Lock(arg,-2)
IF Examine(lock,info)
IF info.direntrytype>0
WriteF('Directory of: \s\n',info.filename)
WHILE ExNext(lock,info)
IF info.direntrytype>0
WriteF('\e[1;32m\l\s[25]\e[0;31m',info.filename)
ELSE
WriteF('\l\s[17] \r\d[7]',info.filename,info.size)
ENDIF
WriteF(IF c++=2 THEN (c:=0)+'\n' ELSE ' ')
ENDWHILE
IF c THEN WriteF('\n')
ELSE
WriteF('No Dir!\n')
ENDIF
ENDIF
UnLock(lock)
ELSE
WriteF('What ?!?\n')
ENDIF
ENDPROC
We need only one module, 'dos/dos.m' to tell us about the object
"fileinfoblock". Then we encounter something new: our variable
"info" is not a pointer, but has as type an object. Such a declaration
gives us space on the stack for a complete fileinfoblock structure.
To get to the directory, we have to try to lock its name, and then
examine it. That's where our "info" block comes in: the function
Examine() fills our structure with data it reads from disk. Before
reading all directory entries in this fashion with ExNext(), we'll
have to check if the block is a directory (if direntrytype>0).
With each entry we display colour #2 for directories, and
normal display (with filesize) for the files. Compare those format-
strings with the output, and with the string-formatting descriptions
in 'reference.doc'. With the variable "c" we check how many columns we
did so far: after every 3 we write a <lf> ('\n').
That's it for the tutorial: to learn more, read all the documentation
thoroughly, and study the examples. But most of all: try your own
programs!